﻿using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Haimen.Excel;

namespace Haimen;

internal class HuallExcelReader(
    LargeExcelReader excelReader) {

  public async Task<LgExcelResult> Read(string path, int sheetIndex, int headerRow, int dataRow, string[] col) {
    using LargeExcelBook book = _excelReader.Read(path);
    LgeSharedStrTable sst = book.SharedStringTable();

    LargeExcelSheet sheet = book.Sheet(sheetIndex + 1);
    using LgeSheetReader reader = sheet.Reader();
    reader.SkipToRow(headerRow);

    Dictionary<string, ColRef> headerDict = await ReadHeader(reader, sst);
    HashSet<string> colFilter = col.Select(c => headerDict[c].Letter).ToHashSet();

    reader.SkipToRow(dataRow);
    List<RowRead> rowList = [];
    do {
      RowRead row = ReadDataRow(reader, colFilter);
      rowList.Add(row);
    } while (reader.ReadRow());

    Dictionary<string, int> resultHeader = headerDict.Values
      .Where(c => colFilter.Contains(c.Letter))
      .OrderBy(c => c.Letter.Length)
      .ThenBy(c => c.Letter)
      .Select((c, i) => (name: c.Val, col: i))
      .ToDictionary(kv => kv.name, kv => kv.col);

    LgExcelRow[] resultRows = rowList
      .Select(r => MakeResultRow(r, sst))
      .ToArray();

    return new(resultHeader, resultRows);
  }

  LgExcelRow MakeResultRow(RowRead row, LgeSharedStrTable sst) {
    var makeVal = (CellRead cell) => {
      object val = cell.Type switch {
        "n" => double.Parse(cell.Text),
        "s" => sst.Get(int.Parse(cell.Text)).Result,
        _ => cell.Text,
      };
      return val;
    };

    return new(row.Cells.Select(makeVal).ToArray());
  }

  record CellRead(string Type, string Text);
  record RowRead(string Num, CellRead[] Cells);
  RowRead ReadDataRow(LgeSheetReader reader, HashSet<string> colFilter) {
    List<CellRead> colResult = new(colFilter.Count);

    LgeRowAttr rowAttr = reader.GetRowAttr();
    while (reader.ReadCell()) {
      LargeExcelCell cell = reader.ConsumeCell();
      if (!colFilter.Contains(cell.ColLetter(rowAttr))) {
        continue;
      }

      colResult.Add(new(cell.Type, cell.Value));
      if (colResult.Count >= colFilter.Count) {
        break;
      }
    }

    return new(rowAttr.Ref, [.. colResult]);
  }

  record ColRef(string Letter, string Val);
  async Task<Dictionary<string, ColRef>> ReadHeader(LgeSheetReader reader, LgeSharedStrTable sst) {
    List<ColRef> colList = [];
    LgeRowAttr rowAttr = reader.GetRowAttr();

    while (reader.ReadCell()) {
      LargeExcelCell cell = reader.ConsumeCell();
      string colLetter = cell.ColLetter(rowAttr);
      string cellVal = await sst.GetVal(cell);
      colList.Add(new(colLetter, cellVal));
    }

    return colList
      .Where(c => !string.IsNullOrWhiteSpace(c.Val))
      .ToDictionary(c => c.Val, c => c);
  }

  readonly LargeExcelReader _excelReader = excelReader;
}

internal record LgExcelRow(object[] ColVal);
internal record LgExcelResult(Dictionary<string, int> HeaderCol, LgExcelRow[] Rows);
